﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BackupAssurance.Library.MEF;
using System.ComponentModel.Composition;
using BackupAssurance.Library.ModelsDB;
using BackupAssurance.Library.Interfaces;
using BackupAssurance.Library.XML;
using BackupAssurance.Library.Constants;
using System.IO;
using BackupAssurance.Library.Enums;

namespace BackupAssurance.impFileSynch
{

    [Export(typeof(IExecute)), ExportMetadata("PlugInType", "FileCopyMove"), ExportMetadata("Version", 1)]
    public class piFileSynch : IExecute
    {

        [Import]
        public IfLogger _objLog { get; set; }

        string _name = null;

        string _includeFilenames = "";
        string _includeFolders = "";
        string _includeExtensions = "";
        string _excludeFilenames = "";
        string _excludeFolders = "";
        string _excludeExtensions = "";

        string[] _aIncludeFilenames;
        string[] _aIncludeFolders;
        string[] _aIncludeExtensions;
        string[] _aExcludeFilenames;
        string[] _aExcludeFolders;
        string[] _aExcludeExtensions;

        StringBuilder _objFilesRejected;
        string _processFolder;
        string _targetFolder;
        int _NumHoursBack;
        Boolean _recurseFolder;
        Boolean _OverrideFileIfExists;
        int _numFilesCopied;
        int _numFilesFailed;
        StringBuilder _objstring;
        bool _IsFirstRun;
        int _DemoMode = 0;
        bool _MoveFileFlag;
        Boolean _UseHourSystem;
        Boolean _CompareEachFile;

        public string Execute(pcJobAction jobaction, pcPlugInValue plugininfo)
        {
            string strResult = "";
            int _DemoMode = 0;
            string output;
            _numFilesFailed = 0;
            _OverrideFileIfExists = false;
            _MoveFileFlag = false;
            _IsFirstRun = false;
            _targetFolder = "";
            _processFolder = "";
            _NumHoursBack = 0;
            _recurseFolder = false;
            _numFilesCopied = 0;
            _objFilesRejected = new StringBuilder();
            _recurseFolder = false;
            _UseHourSystem = false;
            _CompareEachFile = false;

            _objstring = new StringBuilder();
            _DemoMode = plugininfo.DemoMode;
            _name = System.Environment.MachineName + " FileCopyMove v1, START, Job ID:" + jobaction.Id.ToString() + " Plugin ID:" + plugininfo.Id.ToString() + Environment.NewLine + plugininfo.PlugInText;

            _objstring.AppendLine("");
            _objstring.AppendLine("");
            _objstring.AppendLine("Files to Copy today [" + DateTime.Now + "]");
            _objstring.AppendLine("--------------------------------------------------");
            try
            {
                //grab the fields from the XML or whatever.
                Dictionary<string, string> objDic;
                _objLog.WriteLine("Plugin Start: " + _name);

                //objLog.WriteLine(plugininfo.PlugInText);

                objDic = fXMLParser.ConvertXMLtoDictionary(plugininfo.PlugInText);
                _DemoMode = plugininfo.DemoMode;
                _UseHourSystem = Boolean.Parse(objDic[Constants.PLG_USE_HOUR_SYSTEM]);
                _CompareEachFile = Boolean.Parse(objDic[Constants.PLG_COMPARE_EACH_FILE]);
                _MoveFileFlag = Boolean.Parse(objDic[Constants.PLG_MOVE_FILE_FLAG]);
                _processFolder = objDic[Constants.PLG_PROCESSFOLDER];
                _targetFolder = objDic[Constants.PLG_TARGET_FOLDER];
                _NumHoursBack = Int32.Parse(objDic[Constants.PLG_NUMHOURSBACK]);
                _recurseFolder = Boolean.Parse(objDic[Constants.PLG_RECURSEFOLDER]);
                _OverrideFileIfExists = Boolean.Parse(objDic[Constants.PLG_OVERRIDE_FILE]);

                if (_processFolder.Substring(_processFolder.Length - 1, 1) != "\\")
                {
                    _processFolder = _processFolder + "\\";
                }

                if (_targetFolder.Substring(_targetFolder.Length - 1, 1) != "\\")
                {
                    _targetFolder = _targetFolder + "\\";
                }

                _includeFilenames = objDic[Constants.PLG_INCLUDEFILENAMES];
                _includeFolders = objDic[Constants.PLG_INCLUDEFOLDERS];
                _includeExtensions = objDic[Constants.PLG_INCLUDEEXTENSIONS];
                _excludeFilenames = objDic[Constants.PLG_EXCLUDEFILENAMES];
                _excludeFolders = objDic[Constants.PLG_EXCLUEFOLDERS];
                _excludeExtensions = objDic[Constants.PLG_EXCLUDEEXTENSIONS];

                _includeFilenames = _includeFilenames.Trim().ToUpper();
                _includeFolders = _includeFolders.Trim().ToUpper();
                _includeExtensions = _includeExtensions.Trim().ToUpper();
                _excludeFilenames = _excludeFilenames.Trim().ToUpper();
                _excludeFolders = _excludeFolders.Trim().ToUpper();
                _excludeExtensions = _excludeExtensions.Trim().ToUpper();

                _includeExtensions.Replace(".", "");
                _excludeExtensions.Replace(".", "");

                //put the inclusion and exclusions into arrays
                _aIncludeFilenames = _includeFilenames.Split(';');
                _aIncludeFolders = _includeFolders.Split(';');
                _aIncludeExtensions = _includeExtensions.Split(';');

                _aExcludeFilenames = _excludeFilenames.Split(';');
                _aExcludeFolders = _excludeFolders.Split(';');
                _aExcludeExtensions = _excludeExtensions.Split(';');


                if (_processFolder.Substring(_processFolder.Length - 1, 1) != "\\")
                {
                    _processFolder = _processFolder + "\\";
                }


                if (_targetFolder.Substring(_targetFolder.Length - 1, 1) != "\\")
                {
                    _targetFolder = _targetFolder + "\\";
                }

                //---------------------------------------------------------------------------------------------------
                try
                {
                    //check if the process folder exists
                    if (!Directory.Exists(_processFolder))
                    {
                        output = _name + Environment.NewLine + "Process Folder [" + _processFolder + "] does not exist.";
                        _objLog.WriteError(0, output);
                        throw new Exception(output);
                    }
                }
                catch (Exception)
                {
                    output = _name + Environment.NewLine + "Process Folder [" + _processFolder + "] does not exist.";
                    _objLog.WriteError(0, output);
                    throw new Exception(output);
                }

                //---------------------------------------------------------------------------------------------------
                try
                {
                    _IsFirstRun = false;

                    //check if the target folder exists
                    if (!Directory.Exists(_targetFolder))
                    {
                        _IsFirstRun = true;
                        output = _name + Environment.NewLine + "Target Folder [" + _targetFolder + "] does not exist.  It will be created and all files copied.";
                        _objLog.WriteError(0, output);
                        System.IO.Directory.CreateDirectory(_targetFolder);
                    }
                }
                catch (Exception e)
                {
                    output = _name + Environment.NewLine + "Target Folder [" + _targetFolder + "] does not exist and it could not be created. " + e.Message;
                    _objLog.WriteError(0, output);
                    _IsFirstRun = true;
                }

                //---------------------------------------------------------------------------------------------------
                //---------------------------------------------------------------------------------------------------
                try
                {
                    //ok, start to look for files/folders to delete
                    _objLog.WriteLine("Looking for files to copy...");
                    DirectoryInfo di = new DirectoryInfo(_processFolder);
                    WalkDirectoryTree(di);
                }
                catch (Exception e)
                {
                    output = _name + Environment.NewLine + " Unhandled Error trying to determine which files to delete." + Environment.NewLine + e.Message + Environment.NewLine + e.StackTrace;
                    _objLog.WriteError(0, output);
                    throw new Exception(output);
                }
                //---------------------------------------------------------------------------------------------------

                _objstring.AppendLine("");
                _objstring.AppendLine("Files Failed:" + _objFilesRejected.ToString());
                _objstring.AppendLine("--------------------------------------------------");
                _objstring.AppendLine("");
                _objstring.AppendLine("");
                _objstring.AppendLine("Totals");
                _objstring.AppendLine("--------------------------------------------------");
                _objstring.AppendLine("Files Copied:" + _numFilesCopied.ToString());
                _objstring.AppendLine("Files Failed:" + _numFilesFailed.ToString());
                _objstring.AppendLine("");
                _objstring.AppendLine("");

                _objstring.AppendLine("--------------------------------------------------");
                _objstring.AppendLine(_name);
                strResult = _objstring.ToString();
            }
            catch (Exception ex)
            {
                output = _name + Environment.NewLine + ex.Message + Environment.NewLine + ex.StackTrace;
                _objLog.WriteError(0, output);
                throw new Exception(output);

            }

            return strResult;

        }


        private void ProcessFile(FileInfo sourcefile)
        {

            if (FileIsIncluded(sourcefile) && FileIsNotExcluded(sourcefile))
            {

                //now check how old the file is:
                TimeSpan span = DateTime.Now - sourcefile.LastWriteTime;
                double DaysOld = span.TotalHours;
                string thisfiletargetpath = "";
                Boolean passedfilecompare;

                int isourcelength = _processFolder.Length;
                if (sourcefile.DirectoryName.Length <= isourcelength)
                {
                    thisfiletargetpath = _targetFolder;
                }
                else
                {
                    thisfiletargetpath = _targetFolder + sourcefile.DirectoryName.Substring(isourcelength);
                }

                string newfilename = thisfiletargetpath + sourcefile.Name;

                passedfilecompare = false;
                if (_CompareEachFile == true)
                {


                    //check if the file exists.  
                    if (File.Exists(newfilename))
                    {
                        System.IO.FileInfo fTarget;
                        fTarget = new System.IO.FileInfo(newfilename);
                        if ((fTarget.LastWriteTime < sourcefile.LastWriteTime) || (fTarget.Length < sourcefile.Length))
                        {
                            //the file changed since last time, copy it
                            passedfilecompare = true;
                        }
                        else
                        {
                            //the file has not changed, dont copy it.
                            passedfilecompare = false;
                        }
                    }
                    else
                    {
                        //file doesn't exist
                        passedfilecompare = true;
                    }
                }

                if (_IsFirstRun == true || (_CompareEachFile == false || _CompareEachFile == true && passedfilecompare == true) && (_UseHourSystem == false || (_UseHourSystem == true && DaysOld >= _NumHoursBack)))
                {
                    //compare the file if applicable.

                    CopyMoveTheFile(sourcefile, thisfiletargetpath, newfilename, DaysOld);

                }
            }
        }

        void CopyMoveTheFile(FileInfo sourcefile, string thisfiletargetpath, string newfilename, double DaysOld)
        {
            if (_DemoMode == 0)
            {
                // To copy a folder's contents to a new location:
                // Create a new target folder, if necessary.
                //-----------------------------------------------------------------------
                try
                {
                    if (!System.IO.Directory.Exists(thisfiletargetpath))
                    {
                        System.IO.Directory.CreateDirectory(thisfiletargetpath);
                    }
                }
                catch (Exception e)
                {
                    string output = String.Format("Could not create folder while copying file [{0}] to [{1}].  {2}", sourcefile.FullName, newfilename, e.Message);
                    //_objstring.AppendLine(output);
                    _objFilesRejected.AppendLine(output);
                }


                //-----------------------------------------------------------------------
                try
                {

                    System.IO.File.Copy(sourcefile.FullName, newfilename, _OverrideFileIfExists);
                    _numFilesCopied = _numFilesCopied + 1;
                    string output = String.Format("Copied file [{0}], [{1:0.00}] days old and last updated [{2}] to [{3}]", sourcefile.FullName, DaysOld, sourcefile.LastWriteTime.ToString(), newfilename);
                    _objstring.AppendLine(output);
                }
                catch (Exception ex)
                {
                    _numFilesFailed = _numFilesFailed + 1;
                    string output = String.Format("Could not copy file [{0}] to [{1}].  {2}", sourcefile.FullName, newfilename, ex.Message);
                    _objFilesRejected.AppendLine(output);
                    //_objstring.AppendLine(output);
                }
                //-----------------------------------------------------------------------


                if (_MoveFileFlag == true)
                {
                    string strfullname = sourcefile.FullName;

                    try
                    {
                        File.Delete(sourcefile.FullName);
                        string output = String.Format("Deleted source file [{0}]", strfullname);
                        _objstring.AppendLine(output);
                    }
                    catch (Exception er)
                    {
                        string output = String.Format("Could not delete file [{0}].  Error: {1}", strfullname, er.Message);
                        _objFilesRejected.AppendLine(output);
                    }

                }
                //-----------------------------------------------------------------------
            }

        }
        void WalkDirectoryTree(System.IO.DirectoryInfo root)
        {
            string output;
            System.IO.FileInfo[] files = null;
            System.IO.DirectoryInfo[] subDirs = null;

            // First, process all the files directly under this folder
            try
            {
                files = root.GetFiles("*.*");
            }
            // This is thrown if even one of the files requires permissions greater
            // than the application provides.
            catch (UnauthorizedAccessException e)
            {
                // This code just writes out the message and continues to recurse.
                // You may decide to do something different here. For example, you
                // can try to elevate your privileges and access the file again.
                output = _name + Environment.NewLine + " Error:Unauthorized Access.  " + root.ToString() + e.Message + Environment.NewLine + " StackTrace:" + e.StackTrace;
                _objLog.WriteError(0, output);
            }

            catch (System.IO.DirectoryNotFoundException e)
            {
                output = _name + Environment.NewLine + " Error:Directory Not Found.  " + root.ToString() + e.Message + Environment.NewLine + " StackTrace:" + e.StackTrace;
                _objLog.WriteError(0, output);
            }

            if (files != null)
            {
                foreach (System.IO.FileInfo fi in files)
                {
                    // In this example, we only access the existing FileInfo object. If we
                    // want to open, delete or modify the file, then
                    // a try-catch block is required here to handle the case
                    // where the file has been deleted since the call to TraverseTree().

                    try
                    {
                        ProcessFile(fi);
                    }
                    catch (Exception e)
                    {
                        output = _name + Environment.NewLine + " Unhandled Error Professing File  [" + fi.FullName + "] " + Environment.NewLine + e.Message + Environment.NewLine + " StackTrace:" + e.StackTrace;
                        _objLog.WriteError(0, output);
                        //throw new Exception(output);
                    }
                }

                // Now find all the subdirectories under this directory.
                subDirs = root.GetDirectories();
                if (_recurseFolder)
                {
                    foreach (System.IO.DirectoryInfo dirInfo in subDirs)
                    {
                        // Resursive call for each subdirectory.
                        WalkDirectoryTree(dirInfo);
                    }
                }

            }
        }

        public bool IsDirectoryEmpty(string path)
        {
            return !Directory.EnumerateFileSystemEntries(path).Any();
        }


        List<string> removeDuplicates(List<string> inputList)
        {
            Dictionary<string, int> uniqueStore = new Dictionary<string, int>();
            List<string> finalList = new List<string>();

            foreach (string currValue in inputList)
            {
                if (!uniqueStore.ContainsKey(currValue))
                {
                    uniqueStore.Add(currValue, 0);
                    finalList.Add(currValue);
                }
            }
            return finalList;
        }


        private Boolean FileIsIncluded(FileInfo file)
        {
            Boolean bIncludeFilenames = false;
            Boolean bIncludeFolders = false;
            Boolean bIncludeExtensions = false;


            try
            {
                if (_includeFilenames == "" || _includeFilenames == "*")
                {
                    bIncludeFilenames = true;
                }
                else
                {
                    if (file.Extension == "")
                    {
                        bIncludeFilenames = false;
                    }
                    else
                    {
                        string FileName = file.Name.Replace(file.Extension, "");
                        FileName = FileName.ToUpper();
                        bIncludeFilenames = ArrayCompare(FileName, _aIncludeFilenames);
                    }
                }

                if (_includeFolders == "" || _includeFolders == "*")
                {
                    bIncludeFolders = true;
                }
                else
                {
                    bIncludeFolders = ArrayCompare(file.DirectoryName.ToUpper(), _aIncludeFolders);
                }

                if (_includeExtensions == "" || _includeExtensions == "*")
                {
                    bIncludeExtensions = true;
                }
                else
                {
                    if (file.Extension == "")
                    {
                        bIncludeExtensions = false;
                    }
                    else
                    {
                        string sExtension = file.Extension.ToUpper();

                        if ((sExtension != "") && sExtension.Substring(0, 1) == ".")
                        {
                            sExtension = sExtension.Substring(1);
                        }

                        bIncludeExtensions = ArrayCompare(sExtension, _aIncludeExtensions);
                    }
                }
                //Console.WriteLine("{0}, fld:{1}, fle:{2}, ext:{3}", file.Name, bIncludeFolders, bIncludeFilenames, bIncludeExtensions);
            }
            catch (Exception ex)
            {
                string output = _name + Environment.NewLine + " piFileDelete.FileIsIncluded " + file.FullName + " " + ex.Message + Environment.NewLine + ex.StackTrace;
                _objLog.WriteError(0, output);
            }


            //aggregate check
            if ((bIncludeFilenames) && (bIncludeFolders) && (bIncludeExtensions))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        private Boolean FileIsNotExcluded(FileInfo file)
        {
            Boolean bExcludeFilenames = false;
            Boolean bExcludeFolders = false;
            Boolean bExcludeExtensions = false;

            try
            {
                //check the filename
                if (_excludeFilenames == "" || _excludeFilenames == "*")
                {
                    bExcludeFilenames = false;
                }
                else
                {
                    if (file.Extension == "")
                    {
                        bExcludeFilenames = false;
                    }
                    else
                    {
                        string FileName = file.Name.Replace(file.Extension, "");
                        FileName = FileName.ToUpper();
                        bExcludeFilenames = ArrayCompare(FileName, _aExcludeFilenames);
                    }
                }

                //check the folder
                if (_excludeFolders == "" || _excludeFolders == "*")
                {
                    bExcludeFolders = false;
                }
                else
                {
                    bExcludeFolders = ArrayCompare(file.DirectoryName.ToUpper(), _aExcludeFolders);
                }

                //check the extension
                if (_excludeExtensions == "" || _excludeExtensions == "*")
                {
                    bExcludeExtensions = false;
                }
                else
                {
                    if (file.Extension == "")
                    {
                        bExcludeExtensions = false;
                    }
                    else
                    {
                        string Extension = file.Extension.Substring(1);
                        Extension = Extension.ToUpper();
                        bExcludeExtensions = ArrayCompare(Extension, _aExcludeExtensions);
                    }
                }

            }
            catch (Exception ex)
            {
                string output = _name + Environment.NewLine + " piFileDelete.FileIsNotExcluded " + file.FullName + " " + ex.Message + Environment.NewLine + ex.StackTrace;
                _objLog.WriteError(0, output);
            }

            //Console.WriteLine("{0}, fld:{1}, fle:{2}, ext:{3}", file.Name, bExcludeFilenames, bExcludeFolders, bExcludeExtensions);

            //aggregate check
            if ((bExcludeFilenames) || (bExcludeFolders) || (bExcludeExtensions))
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        private Boolean ArrayCompare(string strInFileInfo, string[] thearray)
        {

            TypeFileMask enumMask;
            String strNewfromXML;
            int ilength = 0;
            int iInputLength = strInFileInfo.Length;
            strInFileInfo = strInFileInfo.ToUpper();
            //Boolean returnvalue = false;

            if (strInFileInfo == "")
            {
                return false;
            }

            foreach (string filterfromxml in thearray)
            {
                if (filterfromxml == "")
                {
                    continue;
                }
                enumMask = determinemask(filterfromxml);
                strNewfromXML = filterfromxml.Replace("*", "");
                ilength = strNewfromXML.Length;

                switch (enumMask)
                {
                    case TypeFileMask.none:
                        if (strInFileInfo == strNewfromXML) { return true; }
                        break;
                    case TypeFileMask.left:
                        if (ilength <= iInputLength)
                        {
                            if (strInFileInfo.Substring(iInputLength - ilength, ilength) == strNewfromXML)
                            {
                                return true;
                            }
                        }
                        break;
                    case TypeFileMask.right:
                        if (ilength <= iInputLength)
                        {
                            if (strInFileInfo.Substring(0, ilength) == strNewfromXML)
                            {
                                return true;
                            }
                        }
                        break;
                    case TypeFileMask.both:
                        if (strInFileInfo.IndexOf(strNewfromXML) >= 0)
                        {
                            return true;
                        }
                        break;
                    default:
                        return false;
                }

            }
            return false;
        }

        public void Dispose()
        {

        }


        private TypeFileMask determinemask(string strInput)
        {


            Boolean leftmostison = false;
            Boolean rightmostison = false;
            int lenght = strInput.Length;

            if (strInput.Substring(0, 1) == "*") { leftmostison = true; }
            if (strInput.Substring(lenght - 1, 1) == "*") { rightmostison = true; }

            if ((leftmostison == false) && (rightmostison == false))
            {
                return TypeFileMask.none;
            }
            else if ((leftmostison == true) && (rightmostison == true))
            {
                return TypeFileMask.both;
            }
            else if ((leftmostison == true) && (rightmostison == false))
            {
                return TypeFileMask.left;
            }
            else if ((leftmostison == false) && (rightmostison == true))
            {
                return TypeFileMask.right;
            }
            else
            {
                //should never happen
                return TypeFileMask.none;
            }
        }
    }
}
